package hn.sigit.logic.viewer.toolbox;

import hn.sigit.dao.hnd.ladmshadow.RRRDAO;
import hn.sigit.dao.ladm.external.ExtPartyDAO;
import hn.sigit.logic.general.ResourceBundleHelper;
import hn.sigit.logic.viewer.InteractiveViewerHelper;
import hn.sigit.model.commons.IProperty;
import hn.sigit.model.commons.IRRR;
import hn.sigit.model.commons.IResponsibility;
import hn.sigit.model.commons.IRestriction;
import hn.sigit.model.commons.IRight;
import hn.sigit.model.hnd.ladmshadow.Property;
import hn.sigit.model.hnd.ladmshadow.RRR;
import hn.sigit.model.ladm.external.ExtParty;
import hn.sigit.util.ShareValue;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;


public class RRRHelper implements Serializable {
	private static final long serialVersionUID = 1L;

	private InteractiveViewerHelper interactiveViewerHelper;
	
	private IProperty property;
	private Set<? extends IRRR> rrrSet;
	
	@SuppressWarnings("rawtypes")
	private List<ParcelRRR> rrrsToDelete;
	
	private List<ParcelRight> parcelRightsList;
	private List<ParcelRight> newParcelRightsList;
	private Map<String, RRRTypeGroup<ParcelRight>> parcelRightsGroupsMap;
	private List<RRRTypeGroup<ParcelRight>> parcelRightsGroups;
	private ParcelRight selectedRight;
	
	private List<ParcelRestriction> parcelRestrictionsList;
	private List<ParcelRestriction> newParcelRestrictionsList;
	private Map<String, RRRTypeGroup<ParcelRestriction>> parcelRestrictionsGroupsMap;
	private List<RRRTypeGroup<ParcelRestriction>> parcelRestrictionsGroups;
	private ParcelRestriction selectedRestriction;
	
	private List<ParcelResponsibility> parcelResponsibilitiesList;
	private List<ParcelResponsibility> newParcelResponsibilitiesList;
	private Map<String, RRRTypeGroup<ParcelResponsibility>> parcelResponsibilitiesGroupsMap;
	private List<RRRTypeGroup<ParcelResponsibility>> parcelResponsibilitiesGroups;
	private ParcelResponsibility selectedResponsibility;

	private ExtParty selectedPerson;
	//private SimpleSelection naturalPersonPartySelection;
	//private ExtendedTableDataModel<HND_NaturalPerson> dataModel;
	private List<ExtParty> personList;
	private List<ExtParty> selectedPersonList = new ArrayList<ExtParty>();
	private Object tableState;


	private boolean editingRights;
	private boolean editingRestrictions;
	private boolean editingResponsibilities;
	
	public RRRHelper(InteractiveViewerHelper interactiveViewerHelper) {
		this.interactiveViewerHelper = interactiveViewerHelper;
	}

	public IProperty getProperty() {
		if (property == null)
			property = interactiveViewerHelper.getSelectedProperty();

		return property;
	}
	public void setProperty(Property property) {
		this.property = property;
	}

	public Set<? extends IRRR> getRrrSet() {
		if (rrrSet == null) {
			if (getProperty() != null)
				rrrSet = getProperty().getRrr();
		}
		return rrrSet;
	}
	public void setRrrSet(Set<RRR> rrrSet) {
		this.rrrSet = rrrSet;
	}

	@SuppressWarnings("rawtypes")
	public List<ParcelRRR> getRrrsToDelete() {
		if (rrrsToDelete == null)
			rrrsToDelete = new ArrayList<ParcelRRR>();
		return rrrsToDelete;
	}
	
	public List<ParcelRight> getParcelRightsList() {
		if (parcelRightsList == null) {
			parcelRightsList = new ArrayList<ParcelRight>();
			
			if (getRrrSet() != null) {
				ParcelRight pr;
				for (IRRR rrr : getRrrSet()) {
					pr = null;
					if (rrr instanceof IRight) {
						IRight right = (IRight) rrr;
						ExtParty extParty = right.getParty().getExtParty();
						pr = new ParcelRight(
								interactiveViewerHelper.getPresentationId(), extParty,
								right.getType(), new ShareValue(right.getShare()), right);
					}
					
					if (pr != null)
						parcelRightsList.add(pr);
				}
			}
		}
		
		return parcelRightsList;
	}
	public void setParcelRightsList(List<ParcelRight> parcelRightsList) {
		if (this.parcelRightsList != parcelRightsList) {
			this.parcelRightsList = parcelRightsList;
			refreshRightsGroups();
		}
	}

	private Map<String, RRRTypeGroup<ParcelRight>> getParcelRightsGroupsMap() {
		if (parcelRightsGroupsMap == null) {
			parcelRightsGroupsMap = createRRRTypeGroups(getParcelRightsList());
		}
		
		return parcelRightsGroupsMap;
	}
	public List<RRRTypeGroup<ParcelRight>> getParcelRightsGroups() {
		if (parcelRightsGroups == null) {
			parcelRightsGroups = new ArrayList<RRRTypeGroup<ParcelRight>>();
			
			for (RRRTypeGroup<ParcelRight> rtg : getParcelRightsGroupsMap().values())
				parcelRightsGroups.add(rtg);
		}
		return parcelRightsGroups;
	}
	

	public List<ParcelRight> getNewParcelRightsList() {
		if (newParcelRightsList == null) {
			newParcelRightsList = new ArrayList<ParcelRight>();
		}

		return newParcelRightsList;
	}
	public void setNewParcelRightsList(List<ParcelRight> newParcelRightsList) {
		this.newParcelRightsList = newParcelRightsList;
	}

	public void setSelectedRight(ParcelRight selectedRight) {
		this.selectedRight = selectedRight;
	}
	public ParcelRight getSelectedRight() {
		return selectedRight;
	}

	
	public String modifySelectedRights() {
		RRRHelperLogic.modifySelected(parcelRightsList, newParcelRightsList);
		return null;
	}
	
	public String cancelRightsModification() {
		RRRHelperLogic.cancelRRRsModification(newParcelRightsList);
		return null;
	}

	public String acceptRightsModification() {
		RRRHelperLogic.acceptRRRsModification(parcelRightsList, newParcelRightsList);
		refreshRightsGroups();
		return null;
	}
	
	public String cancelRightChanges() {
		RRRDAO.clear();

		setParcelRightsList(null);
		editingRights = false;
		newParcelRightsList = null;
		rrrSet = null;
		property = null;
		
		return null;
	}
	
	public String applyRightChanges() {
		List<String> msgList = new ArrayList<String>();
		if ( areRRRsOK(getParcelRightsList(), msgList) ) {
			RRRHelperLogic.applyRRRChanges(getProperty(), getRrrSet(), getParcelRightsList(), getRrrsToDelete());
	
			setParcelRightsList(null);
			editingRights = false;
			newParcelRightsList = null;
		}
		else {
			for (String msg : msgList)
				FacesContext.getCurrentInstance().addMessage("",
						new FacesMessage(FacesMessage.SEVERITY_ERROR,
								msg,
								""));
		}
		return null;
	}

	public String deleteSelectedRights() {
		RRRHelperLogic.deleteSelectedRRRs(parcelRightsList, getRrrsToDelete());
		refreshRightsGroups();
		return null;
	}
	
	public void addNewRight() {
		if (null == newParcelRightsList || null == getSelectedPerson()) return;
		
		selectedRight = new ParcelRight(interactiveViewerHelper.getPresentationId(), getSelectedPerson());
		selectedRight.setNewRRR(true);
		
		newParcelRightsList.add(selectedRight);
	}

	public ShareValue getRightsSumShares() {
		return RRRHelperLogic.getSumShares(parcelRightsList);
	}
	
	public ShareValue getRightsSumNewShares() {
		return RRRHelperLogic.getSumShares(newParcelRightsList);
	}
	
	private void refreshRightsGroups() {
		parcelRightsGroupsMap = null;
		parcelRightsGroups = null;
	}
	
	
	/******
	 * BEGIN Properties for RESTRICTIONS
	 ****/
	public List<ParcelRestriction> getParcelRestrictionsList() {
		if (parcelRestrictionsList == null) {
			parcelRestrictionsList = new ArrayList<ParcelRestriction>();
			
			if (getRrrSet() != null) {
				ParcelRestriction pr;
				for (IRRR rrr : getRrrSet()) {
					pr = null;
					if (rrr instanceof IRestriction) {
						IRestriction restriction = (IRestriction) rrr;
						ExtParty extParty = restriction.getParty().getExtParty();
						//long presentationId, ExtParty extParty, ShareValue share, Restriction restriction
						pr = new ParcelRestriction(
								interactiveViewerHelper.getPresentationId(),
								extParty,
								restriction.getType(),
								new ShareValue(restriction.getShare()), restriction);
					}
					
					if (pr != null)
						parcelRestrictionsList.add(pr);
				}
			}
		}

		return parcelRestrictionsList;
	}
	public void setParcelRestrictionsList(List<ParcelRestriction> parcelRestrictionsList) {
		if (this.parcelRestrictionsList != parcelRestrictionsList) {
			this.parcelRestrictionsList = parcelRestrictionsList;
			refreshRestrictionsGroups();
		}
	}

	private Map<String, RRRTypeGroup<ParcelRestriction>> getParcelRestrictionsGroupsMap() {
		if (parcelRestrictionsGroupsMap == null) {
			parcelRestrictionsGroupsMap = createRRRTypeGroups(getParcelRestrictionsList());
		}
		return parcelRestrictionsGroupsMap;
	}
	
	public List<RRRTypeGroup<ParcelRestriction>> getParcelRestrictionsGroups() {
		if (parcelRestrictionsGroups == null) {
			parcelRestrictionsGroups = new ArrayList<RRRTypeGroup<ParcelRestriction>>();
			
			for (RRRTypeGroup<ParcelRestriction> rtg : getParcelRestrictionsGroupsMap().values())
				parcelRestrictionsGroups.add(rtg);
		}
		return parcelRestrictionsGroups;
	}

	public List<ParcelRestriction> getNewParcelRestrictionsList() {
		if (newParcelRestrictionsList == null) {
			newParcelRestrictionsList = new ArrayList<ParcelRestriction>();
		}
		return newParcelRestrictionsList;
	}
	public void setNewParcelRestrictionsList(List<ParcelRestriction> newParcelRestrictionsList) {
		this.newParcelRestrictionsList = newParcelRestrictionsList;
	}

	public ParcelRestriction getSelectedRestriction() {
		return selectedRestriction;
	}
	public void setSelectedRestriction(ParcelRestriction selectedRestriction) {
		this.selectedRestriction = selectedRestriction;
	}
	
	public String modifySelectedRestrictions() {
		RRRHelperLogic.modifySelected(parcelRestrictionsList, newParcelRestrictionsList);
		return null;
	}
	
	public String cancelRestrictionsModification() {
		RRRHelperLogic.cancelRRRsModification(newParcelRestrictionsList);
		return null;
	}

	public String acceptRestrictionsModification() {
		RRRHelperLogic.acceptRRRsModification(parcelRestrictionsList, newParcelRestrictionsList);
		refreshRestrictionsGroups();
		return null;
	}
	
	public String cancelRestrictionChanges() {
		RRRDAO.clear();

		setParcelRestrictionsList(null);
		editingRestrictions = false;
		newParcelRestrictionsList = null;
		rrrSet = null;
		property = null;
		
		return null;
	}

	public String applyRestrictionChanges() {
		List<String> msgList = new ArrayList<String>();
		if ( areRRRsOK(getParcelRestrictionsList(), msgList) ) {
			RRRHelperLogic.applyRRRChanges(getProperty(), getRrrSet(), getParcelRestrictionsList(), getRrrsToDelete());
	
			setParcelRestrictionsList(null);
			editingRestrictions = false;
			newParcelRestrictionsList = null;
		}
		else {
			for (String msg : msgList)
				FacesContext.getCurrentInstance().addMessage("",
						new FacesMessage(FacesMessage.SEVERITY_ERROR,
								msg,
								""));
		}
		return null;
	}

	public String deleteSelectedRestrictions() {
		RRRHelperLogic.deleteSelectedRRRs(parcelRestrictionsList, getRrrsToDelete());
		refreshRestrictionsGroups();
		return null;
	}
	
	public void addNewRestriction() {
		if (null == newParcelRestrictionsList || null == getSelectedPerson()) return;
		
		selectedRestriction = new ParcelRestriction(interactiveViewerHelper.getPresentationId(), getSelectedPerson());
		selectedRestriction.setNewRRR(true);
		
		newParcelRestrictionsList.add(selectedRestriction);
	}

	public ShareValue getRestrictionsSumShares() {
		return RRRHelperLogic.getSumShares(parcelRestrictionsList);
	}
	
	public ShareValue getRestrictionsSumNewShares() {
		return RRRHelperLogic.getSumShares(newParcelRestrictionsList);
	}

	private void refreshRestrictionsGroups() {
		parcelRestrictionsGroupsMap = null;
		parcelRestrictionsGroups = null;
	}

	/******
	 * END Properties for RESTRICTIONS
	 ****/

	/******
	 * BEGIN Properties for RESPONSIBILITIES
	 ****/
	public List<ParcelResponsibility> getParcelResponsibilitiesList() {
		if (parcelResponsibilitiesList == null) {
			parcelResponsibilitiesList = new ArrayList<ParcelResponsibility>();

			if (getRrrSet() != null) {
				ParcelResponsibility pr;
				for (IRRR rrr : getRrrSet()) {
					pr = null;
					if (rrr instanceof IResponsibility) {
						IResponsibility responsibility = (IResponsibility) rrr;
						ExtParty extParty = responsibility.getParty().getExtParty();
						pr = new ParcelResponsibility(
								interactiveViewerHelper.getPresentationId(),
								extParty,
								responsibility.getType(),
								new ShareValue(responsibility.getShare()),
								responsibility);
					}
					
					if (pr != null)
						parcelResponsibilitiesList.add(pr);
				}
			}
		}
		
		return parcelResponsibilitiesList;
	}
	public void setParcelResponsibilitiesList(List<ParcelResponsibility> parcelResponsibilitiesList) {
		if (this.parcelResponsibilitiesList != parcelResponsibilitiesList) {
			this.parcelResponsibilitiesList = parcelResponsibilitiesList;
			refreshResponsibilitiesGroups();
		}
	}

	private Map<String, RRRTypeGroup<ParcelResponsibility>> getParcelResponsibilitiesGroupsMap() {
		if (parcelResponsibilitiesGroupsMap == null) {
			parcelResponsibilitiesGroupsMap = createRRRTypeGroups(getParcelResponsibilitiesList());
		}
		return parcelResponsibilitiesGroupsMap;
	}
	
	public List<RRRTypeGroup<ParcelResponsibility>> getParcelResponsibilitiesGroups() {
		if (parcelResponsibilitiesGroups == null) {
			parcelResponsibilitiesGroups = new ArrayList<RRRTypeGroup<ParcelResponsibility>>();
			
			for (RRRTypeGroup<ParcelResponsibility> rtg : getParcelResponsibilitiesGroupsMap().values())
				parcelResponsibilitiesGroups.add(rtg);
		}
		return parcelResponsibilitiesGroups;
	}

	public List<ParcelResponsibility> getNewParcelResponsibilitiesList() {
		if (newParcelResponsibilitiesList == null) {
			newParcelResponsibilitiesList = new ArrayList<ParcelResponsibility>();
		}
		return newParcelResponsibilitiesList;
	}
	public void setNewParcelResponsibilitiesList(List<ParcelResponsibility> newParcelResponsibilitiesList) {
		this.newParcelResponsibilitiesList = newParcelResponsibilitiesList;
	}
	
	public ParcelResponsibility getSelectedResponsibility() {
		return selectedResponsibility;
	}
	public void setSelectedResponsibility(ParcelResponsibility selectedResponsibility) {
		this.selectedResponsibility = selectedResponsibility;
	}
	
	public String modifySelectedResponsibilities() {
		RRRHelperLogic.modifySelected(parcelResponsibilitiesList, newParcelResponsibilitiesList);
		return null;
	}
	
	public String cancelResponsibilitiesModification() {
		RRRHelperLogic.cancelRRRsModification(newParcelResponsibilitiesList);
		return null;
	}

	public String acceptResponsibilitiesModification() {
		RRRHelperLogic.acceptRRRsModification(parcelResponsibilitiesList, newParcelResponsibilitiesList);
		refreshResponsibilitiesGroups();
		return null;
	}
	
	public String cancelResponsibilityChanges() {
		RRRDAO.clear();

		setParcelResponsibilitiesList(null);
		editingResponsibilities = false;
		newParcelResponsibilitiesList = null;
		rrrSet = null;
		property = null;
		
		return null;
	}

	public String applyResponsibilityChanges() {
		List<String> msgList = new ArrayList<String>();
		if ( areRRRsOK(getParcelResponsibilitiesList(), msgList) ) {
			RRRHelperLogic.applyRRRChanges(getProperty(), getRrrSet(), getParcelResponsibilitiesList(), getRrrsToDelete());
	
			setParcelResponsibilitiesList(null);
			editingResponsibilities = false;
			newParcelResponsibilitiesList = null;
		}
		else {
			for (String msg : msgList)
				FacesContext.getCurrentInstance().addMessage("",
						new FacesMessage(FacesMessage.SEVERITY_ERROR,
								msg,
								""));
		}
		return null;
	}

	public String deleteSelectedResponsibilities() {
		RRRHelperLogic.deleteSelectedRRRs(parcelResponsibilitiesList, getRrrsToDelete());
		refreshResponsibilitiesGroups();
		return null;
	}
	
	public void addNewResponsibility() {
		if (null == newParcelResponsibilitiesList || null == getSelectedPerson()) return;
		
		selectedResponsibility = new ParcelResponsibility(interactiveViewerHelper.getPresentationId(), getSelectedPerson());
		selectedResponsibility.setNewRRR(true);
		
		newParcelResponsibilitiesList.add(selectedResponsibility);
	}

	public ShareValue getResponsibilitiesSumShares() {
		return RRRHelperLogic.getSumShares(parcelResponsibilitiesList);
	}
	
	public ShareValue getResponsibilitiesSumNewShares() {
		return RRRHelperLogic.getSumShares(newParcelResponsibilitiesList);
	}

	private void refreshResponsibilitiesGroups() {
		parcelResponsibilitiesGroupsMap = null;
		parcelResponsibilitiesGroups = null;
	}
	
	/******
	 * END Properties for RESPONSIBILITIES
	 ****/

	public ExtParty getSelectedPerson() {
		return selectedPerson;
	}
	public void setSelectedPerson(ExtParty party) {
		selectedPerson = party;
	}

	public List<ExtParty> getPersonList() {
		if (personList == null) {
			personList = ExtPartyDAO.loadExtParties();
		}
		return personList;
	}
	
	public List<ExtParty> getSelectedPersonList() {
		return selectedPersonList;
	}
	public void setSelectedNaturalPersonParties(
			List<ExtParty> selectedPersonList) {
		this.selectedPersonList = selectedPersonList;
	}
	
	public Object getTableState() {
		return tableState;
	}
	public void setTableState(Object tableState) {
		this.tableState = tableState;
	}
	
	public boolean isEditingRights() {
		return editingRights;
	}
	public void setEditingRights(boolean editingRights) {
		this.editingRights = editingRights;
	}

	public boolean isEditingRestrictions() {
		return editingRestrictions;
	}
	public void setEditingRestrictions(boolean editingRestrictions) {
		this.editingRestrictions = editingRestrictions;
	}

	public boolean isEditingResponsibilities() {
		return editingResponsibilities;
	}
	public void setEditingResponsibilities(boolean editingResponsibilities) {
		this.editingResponsibilities = editingResponsibilities;
	}

	public boolean isEditing() {
		return editingRights || editingRestrictions || editingResponsibilities;
	}
	
	
	private <PR extends ParcelRRR<?>> boolean areRRRsOK(List<PR> listToCheck, List<String> msgList) {
		boolean retval = true;
		String rrrType;
		ShareValue rrrValue;
		Map<String, ShareValue> rrrTypeToRRRMap = new HashMap<String, ShareValue>();
		for (ParcelRRR<?> pr : listToCheck) {
			rrrType = pr.getTypeString(); 
			
			if (!rrrTypeToRRRMap.containsKey(rrrType))
				rrrTypeToRRRMap.put(rrrType, new ShareValue());
			
			rrrValue = rrrTypeToRRRMap.get(rrrType);
			rrrValue.addOther(pr.getShare());
		}
		
		for (Map.Entry<String, ShareValue> entry : rrrTypeToRRRMap.entrySet())
			if (!entry.getValue().isOne()) {
				retval = false;
				ResourceBundleHelper rbh = interactiveViewerHelper.getResBundle();
				if (msgList == null)
					return false;
				else
					msgList.add(
							String.format(rbh.loadMessage("dataentry.mutate_rrr.rrr_sumtype_not_one.fmt"),
									rbh.loadMessage(entry.getKey())));
			}

		return retval;
	}

	private <PR extends ParcelRRR<?>> Map<String, RRRTypeGroup<PR>> createRRRTypeGroups(List<PR> parcelRRRList) {
		Map<String, RRRTypeGroup<PR>> retval = new LinkedHashMap<String, RRRHelper.RRRTypeGroup<PR>>();
		String key;
		RRRTypeGroup<PR> rrrTypeGroup;
		
		for (PR pr : parcelRRRList) {
			key = pr.getTypeString();
			
			rrrTypeGroup = retval.get(key);
			if (rrrTypeGroup == null) {
				rrrTypeGroup = new RRRTypeGroup<PR>(key);
				retval.put(key, rrrTypeGroup);
			}
			rrrTypeGroup.add(pr);
		}
		
		return retval;
	}
	
	public class RRRTypeGroup<PR extends ParcelRRR<?>> {
		private String rrrType;
		private List<PR> rrrList;
		private ShareValue sumShares;
		
		public RRRTypeGroup(String rrrType) {
			this.rrrType = rrrType;
			this.rrrList = new ArrayList<PR>();
			this.sumShares = new ShareValue();
		}
		
		public String getRrrType() {
			return rrrType;
		}
		
		public List<PR> getRrrList() {
			return Collections.unmodifiableList(rrrList);
		}

		public ShareValue getSumShares() {
			return sumShares;
		}

		public void add(PR parcelRRR) {
			if (parcelRRR.getTypeString().equals(rrrType)
					&& !rrrList.contains(parcelRRR)) {
				rrrList.add(parcelRRR);
				sumShares.addOther(parcelRRR.getShare());
			}
		}
		
		public void remove(PR parcelRRR) {
			if (rrrList.contains(parcelRRR)) {
				rrrList.remove(parcelRRR);
				sumShares.addOther(parcelRRR.getShare().opposite());
			}
		}
		
		public void clear() {
			rrrList.clear();
			sumShares.setToZero();
		}
	}
}
